package com.ruoyi.charge.manager;

import com.ruoyi.charge.clinet.FrameCodec;
import com.ruoyi.charge.model.BillingModelPayload;
import com.ruoyi.charge.model.ChargingPile;
import com.ruoyi.charge.model.DataMessage;
import com.ruoyi.charge.model.ManageWebControllerStartPayload;
import com.ruoyi.charge.protocol.IClientService;
import com.ruoyi.common.constants.CacheConstant;
import com.ruoyi.common.utils.BcdCodeTransfer;
import com.ruoyi.common.utils.RedisUtil;
import io.vertx.core.Vertx;
import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetClientOptions;
import io.vertx.core.net.NetSocket;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

@Component
public class ChargingPileManager {
    private static final Logger logger = LoggerFactory.getLogger(ChargingPileManager.class);
    private final Map<String, ChargingPile> chargingPiles = new HashMap<>();
    private static final int HEARTBEAT_INTERVAL = 10000; // 心跳间隔10秒
    private static final int RECONNECT_DELAY = 5000; // 重连延迟5秒
    @Inject
    private Vertx vertx;
    @Inject
    private IClientService clientService;
    @Inject
    private RedisUtil redisUtil;

    public void registerChargingPile(String sn, String host, int port) {
        if (chargingPiles.containsKey(sn)) {
            logger.warn("充电桩[{}]已存在", sn);
            return;
        }
        ChargingPile chargingPile = new ChargingPile();
        chargingPile.setSn(sn);

        NetClientOptions options = new NetClientOptions()
                .setConnectTimeout(5000)
                .setTcpKeepAlive(true)
                .setReconnectAttempts(3)
                .setReconnectInterval(2000);

        NetClient netClient = vertx.createNetClient(options);
        chargingPile.setNetClient(netClient);
        netClient.connect(port, host, result -> {
            if (result.succeeded()) {
                logger.info("[{}] Connected to {}:{}", sn, host, port);
                chargingPile.setSocket(result.result());
                chargingPile.setConnected(true);
                setupSocketHandlers(chargingPile);
                startHeartbeatTimer(chargingPile);
                clientService.login(chargingPile.getSocket(), chargingPile.getSn());
            } else {
                logger.error("充电桩[{}]连接平台失败: {}:{}", sn, host, port, result.cause());
                scheduleReconnect(chargingPile);
            }
        });

        chargingPiles.put(sn, chargingPile);
    }

    private void setupSocketHandlers(ChargingPile chargingPile) {
        NetSocket socket = getNetSocket(chargingPile);

        socket.closeHandler(v -> {
            logger.info("充电桩[{}]连接已关闭", chargingPile.getSn());
            cleanup(chargingPile);
            scheduleReconnect(chargingPile);
        });

        socket.exceptionHandler(e -> {
            logger.error("充电桩[{}]连接异常", chargingPile.getSn(), e);
            cleanup(chargingPile);
            scheduleReconnect(chargingPile);
        });
    }

    private NetSocket getNetSocket(ChargingPile chargingPile) {
        NetSocket socket = chargingPile.getSocket();
        socket.handler(buffer -> {
            byte[] data = buffer.getBytes();
//            logger.info("充电桩[{}]接收到数据: {}", chargingPile.getSn(), HexUtil.encodeHexStr(data));
            DataMessage dataMessage = FrameCodec.decodeFrame(data);
            if (dataMessage == null) {
                logger.error("充电桩[{}]数据解析失败", chargingPile.getSn());
                return;
            }
            handleReceivedData(chargingPile, dataMessage);
        });
        return socket;
    }

    private void handleReceivedData(ChargingPile chargingPile, DataMessage dataMessage) {
        // 处理接收到的数据
        switch (dataMessage.getType()) {
            case 0x02:
                // 处理登录认证应答
                handleLoginResponse(chargingPile, dataMessage);
                break;
            case 0x06:
                // 处理计费模型验证请求应答
                handleChargingModelValidResponse(chargingPile, dataMessage);
                break;
            case 0x0A:
                // 处理计费模型请求应答
                handleChargingModelResponse(dataMessage);
                break;
            case 0x34:
                // 处理运营平台控制启机
                handleManageWebCommandStart(chargingPile, dataMessage);
                break;
            case  0x36:
                // 处理运营平台控制停机
                handleManageWebCommandStop(chargingPile, dataMessage);
                break;

        }
    }

    /**
     * 处理运营平台控制停机
     */
    private void handleManageWebCommandStop(ChargingPile chargingPile, DataMessage dataMessage) {
        byte[] payload = dataMessage.getPayload();
        // 1. 交易流水号 BCD 码 16位
        byte[] tradeSerialNumberBytes = new byte[7];
        System.arraycopy(payload, 0, tradeSerialNumberBytes, 0, 7);
        String tradeSerialNumber = BcdCodeTransfer.bcdToDecimalString(tradeSerialNumberBytes);
        // 2.

    }

    /**
     * 处理运营平台控制启机
     */
    private void handleManageWebCommandStart(ChargingPile chargingPile, DataMessage dataMessage) {
        ManageWebControllerStartPayload payload = ManageWebControllerStartPayload.fromBytes(dataMessage.getPayload());
        logger.info("充电桩[{}]接收到运营平台控制启机指令:{}", payload.getSn(), payload);
        clientService.remoteChargeStartReply(chargingPile.getSocket(),payload);

    }

    /**
     * 处理计费模型请求应答
     */
    private void handleChargingModelResponse(DataMessage dataMessage) {
        BillingModelPayload payload = BillingModelPayload.fromBytes(dataMessage.getPayload());
        logger.info("充电桩[{}]计费模型请求应答:{}", payload.getSn(), payload);
        // 保存计费模型信息
        redisUtil.setCacheObject(CacheConstant.getChargerBillingModel(payload.getSn()), payload);
    }

    private void handleChargingModelValidResponse(ChargingPile chargingPile, DataMessage dataMessage) {
        logger.info("充电桩[{}]计费模型请求成功", chargingPile.getSn());
        byte[] payload = dataMessage.getPayload();
        // 计费模型编号 paload 最后2字节为计费模型编号
        byte[] modelId = new byte[2];
        System.arraycopy(payload, payload.length - 3, modelId, 0, 2);
        String modelIdStr = BcdCodeTransfer.bcdToDecimalString(modelId);
        byte result = payload[payload.length - 1];
        logger.info("充电桩[{}]计费模型编号: {}，验证结果:{}", chargingPile.getSn(), modelIdStr, result);

        // todo 根据计费模型编号进行后续处理(这里为了方便，直接请求)
        clientService.requestChargerChargingModel(chargingPile.getSocket(), chargingPile.getSn());
    }

    private void handleLoginResponse(ChargingPile chargingPile, DataMessage dataMessage) {
        logger.info("充电桩[{}]登录认证成功", chargingPile.getSn());
        // 请求计费模型
        clientService.requestChargingModel(chargingPile.getSocket(), chargingPile.getSn());
    }

    private void startHeartbeatTimer(ChargingPile chargingPile) {
        if (chargingPile.getHeartbeatTimerId() == -1) {
            chargingPile.setHeartbeatTimerId(vertx.setPeriodic(HEARTBEAT_INTERVAL, id -> sendHeartbeat(chargingPile)));
            logger.info("充电桩[{}]心跳定时器已启动，间隔: {}ms", chargingPile.getSn(), HEARTBEAT_INTERVAL);
        }
    }

    private void sendHeartbeat(ChargingPile chargingPile) {
        // 实现心跳发送逻辑
        clientService.ping(chargingPile.getSocket(), chargingPile.getSn());
    }

    private void scheduleReconnect(ChargingPile chargingPile) {
        if (chargingPile.getReconnectTimerId() == -1) {
            if (chargingPile.isNeedReconnect()) {
                logger.info("充电桩[{}]将在{}ms后尝试重连", chargingPile.getSn(), RECONNECT_DELAY);
                chargingPile.setReconnectTimerId(vertx.setTimer(RECONNECT_DELAY, id -> {
                    chargingPile.setReconnectTimerId(-1);
                    connect(chargingPile);
                }));
            }
        }
    }

    private void connect(ChargingPile chargingPile) {
        // 实现重新连接逻辑
    }

    private void cleanup(ChargingPile chargingPile) {
        stopHeartbeatTimer(chargingPile);
        stopReconnectTimer(chargingPile);
        if (chargingPile.getSocket() != null) {
            chargingPile.getSocket().close();
            chargingPile.setSocket(null);
        }
        if (chargingPile.getNetClient() != null) {
            chargingPile.getNetClient().close();
            chargingPile.setNetClient(null);
        }
        chargingPile.setConnected(false);
        chargingPile.setNeedReconnect(false);
    }

    private void stopHeartbeatTimer(ChargingPile chargingPile) {
        if (chargingPile.getHeartbeatTimerId() != -1) {
            vertx.cancelTimer(chargingPile.getHeartbeatTimerId());
            chargingPile.setHeartbeatTimerId(-1);
            logger.info("充电桩[{}]心跳定时器已停止", chargingPile.getSn());
        }
    }

    private void stopReconnectTimer(ChargingPile chargingPile) {
        if (chargingPile.getReconnectTimerId() != -1) {
            vertx.cancelTimer(chargingPile.getReconnectTimerId());
            chargingPile.setReconnectTimerId(-1);
        }
    }

    public void unregisterChargingPile(String pileId) {
        ChargingPile chargingPile = chargingPiles.remove(pileId);
        if (chargingPile != null) {
            cleanup(chargingPile);
            logger.info("充电桩[{}]已注销", pileId);
        }
    }

    public ChargingPile getChargingPile(String pileId) {
        return chargingPiles.get(pileId);
    }
}